package com.floorplanner.lib3ds.tracks
{
	import com.floorplanner.lib3ds.Lib3dsCore;
	import com.floorplanner.lib3ds.Lib3dsIO;
	import com.floorplanner.lib3ds.tracks.*;
	
	/**
	 * Vector track 
	 */
	public class Lib3dsLin3Track extends Lib3dsCore
	{
		public var flags:int;	// int
		public var keyL:Lib3dsLin3Key;
		
		/**
		 * 
		 */
		public function Lib3dsLin3Track()
		{	
			super(true);
		}
		
		/**
		 * 
		 * @param	io
		 */
		public function read( io:Lib3dsIO ):void
		{	
			this.flags = io.readWord();
			io.readDWord();
			io.readDWord();
			
			debug( "Lib3dsLin3Track#flags = " + this.flags );
			
			var keys:int = io.readInt();
			
			for( var i:int = 0; i < keys; ++i ) 
			{
				var k:Lib3dsLin3Key = new Lib3dsLin3Key();
				k.tcb = new Lib3dsTcb();
				k.tcb.read( io );
				
				k.value = new Array( 3 );
				k.value[0] = io.readFloat();
				k.value[1] = io.readFloat();
				k.value[2] = io.readFloat();
				
				insertKey( k );
			}
			
			debug( "got #" + countKeys() + " keys" );
			
			setupTrack();
		}
		
		/**
		 * 
		 * @return
		 */
		private function countKeys():uint
		{
			var cnt:uint = 0;
			var p:Lib3dsLin3Key = this.keyL;
			do
			{
				cnt++;
				p = p.next;
			} while( p is Lib3dsLin3Key );
			return cnt;
		}
		
		/**
		 * inserts a key.
		 * 
		 * @param	key
		 */
		private function insertKey( key:Lib3dsLin3Key ):void
		{			
			if( this.keyL is Lib3dsLin3Key )
			{
				var p:Lib3dsLin3Key = this.keyL;
				
				while( p.next is Lib3dsLin3Key )
					p = p.next;
				p.next = key;
			}
			else
			{
				this.keyL = key;
			}	
		}
		
		/**
		 * 
		 * @param	p
		 * @param	pc
		 * @param	c
		 * @param	nc
		 * @param	n
		 * @param	out
		 */
		private function getTcb( p:Lib3dsTcb, pc:Lib3dsTcb, c:Lib3dsTcb, nc:Lib3dsTcb, n:Lib3dsTcb, out:Object ):void
		{
			var tm:Number,cm:Number,cp:Number,bm:Number,bp:Number,tmcm:Number,tmcp:Number,cc:Number;
			var dt:Number,fp:Number,fn:Number;
			
			pc = pc is Lib3dsTcb ? pc : c;
			nc = nc is Lib3dsTcb ? nc : c;
			
			fp = fn = 1.0;
			
			if( p && n ) 
			{
				dt = 0.5 * (pc.frame - p.frame + n.frame - nc.frame);
				fp = (pc.frame - p.frame) / dt;
				fn = (n.frame - nc.frame) / dt;
				cc = Math.abs(c.cont);
				fp = fp + cc - cc * fp;
				fn = fn + cc - cc * fn;
			}
			
			cm = 1.0 - c.cont;
			tm = 0.5 * (1.0 - c.tens);
			cp = 2.0- cm;
			bm = 1.0 - c.bias;
			bp = 2.0 - bm;      
			tmcm = tm * cm;
			tmcp = tm * cp;
			
			out.ksm = tmcm * bp * fp;
			out.ksp = tmcp * bm * fp;
			out.kdm = tmcp * bp * fn;
			out.kdp = tmcm * bm * fn;
		}
		
		/**
		 * 
		 * @param	p
		 * @param	cp
		 * @param	c
		 * @param	cn
		 * @param	n
		 */
		private function setupKey( p:Lib3dsLin3Key, cp:Lib3dsLin3Key, c:Lib3dsLin3Key, 
								   cn:Lib3dsLin3Key, n:Lib3dsLin3Key ):void
		{
			if( !c ) throw new Error( "setupKey: param 3 not set!" );
			
			var np:Array = [0,0,0];
			var nn:Array = [0,0,0];
			
			cp = cp is Lib3dsLin3Key ? cp : c;
			cn = cn is Lib3dsLin3Key ? cn : c;
			
			if( !p && !n )
			{
				c.ds = [0,0,0];
				c.dd = [0,0,0];
				return;
			}
			
			var i:int;
			var tcb:Object = new Object();

			if( p && n )
			{
				getTcb(p.tcb, cp.tcb, c.tcb, cn.tcb, n.tcb, tcb);
				vector_sub(np, c.value, p.value);
				vector_sub(nn, n.value, c.value); 
				
				for( i = 0; i < 3; i++ ) 
				{
					c.ds[i] = tcb.ksm * np[i] + tcb.ksp * nn[i];
					c.dd[i] = tcb.kdm * np[i] + tcb.kdp * nn[i];
				}
			}
			else
			{
				if( p ) 
				{
					vector_sub(np, c.value, p.value);
					for( i = 0; i < 3; i++ ) 
					{
						c.ds[i] = np[i];
						c.dd[i] = np[i];
					}
				}
				if( n ) 
				{
					vector_sub(nn, n.value, c.value);
					for( i = 0; i < 3; i++ ) 
					{
						c.ds[i] = nn[i];
						c.dd[i] = nn[i];
					}
				}
			}
			
			//debug( "key for frame #" + c.tcb.frame + " pos:" + c.value + " ds:" + c.ds + " dd:"+c.dd );
		}
		
		/**
		 * creates the keyframe verts etc.
		 */
		private function setupTrack():void
		{
			var pp:Lib3dsLin3Key;
			var pc:Lib3dsLin3Key;
			var pn:Lib3dsLin3Key;
			var pl:Lib3dsLin3Key;
			
			pc = this.keyL;
			
			if( !pc )
			{
				return;
			}
			
			if( !pc.next )
			{
				pc.dd = [0, 0, 0];
				pc.ds = [0, 0, 0];
				return;
			}
			
			if( this.flags & Lib3dsTrackFlags.SMOOTH )
			{
				for( pl = this.keyL; pl.next.next is Lib3dsLin3Key; pl = pl.next )
					setupKey(pl, pl.next, pc, null, pc.next);
			}
			else
			{
				setupKey(null, null, pc, null, pc.next);
			}
			
			for (;;) 
			{
				pp = pc;
				pc = pc.next;
				pn = pc.next;
				if( !pn ) 
				{
					break;
				}
				setupKey(pp, null, pc, null, pn);
			}
			
			if( this.flags & Lib3dsTrackFlags.SMOOTH )
				setupKey(pp, null, pc, this.keyL, this.keyL.next);
			else
				setupKey(pp, null, pc, null, null);
		}
	}
}
